﻿using System.Data;
using System.Diagnostics;
using TPXSDK.NET.GIS;
using TPXSDK.NET.UObject;

namespace TPXSDK.NET.Log;

/// <summary>
/// 拍照点信息
/// </summary>
public class SnapPoint
{
    public ushort SN;
    public int Lng;
    public int Lat;
    public float Alt; //meter

    public short Roll;
    public short Pitch;
    public ushort Yaw;
    public uint TS;
}

/// <summary>
/// 异步事件日志解析器
/// </summary>
public class LogParserEvent : LogParserBase
{
    #region 成员变量
    /// <summary>
    /// 时间戳0对应的绝对时间
    /// </summary>
    public DateTime OriTime { get; private set; } = DateTime.MinValue;

    /// <summary>
    /// 无人机类型
    /// </summary>
    public UDevType UavType { get; set; }

    /// <summary>
    /// POS数据集合
    /// </summary>
    public List<SnapPoint> PosData { get; set; }

    /// <summary>
    /// HOME点位置集合[可能存在多个，需要根据时间戳查询]
    /// </summary>
    public List<Tuple<uint, AbsPos>> HomeLoc { get; set; }

    #endregion

    public LogParserEvent()
    {
        HomeLoc = [];
        PosData = [];
    }

    /// <summary>
    /// 根据时间戳获得指定Home点
    /// </summary>
    public AbsPos? GetHomeLoc(uint ts)
    {
        if (null == HomeLoc || HomeLoc.Count == 0)
        {
            return null;
        }

        var index = -1;

        for ( var i = 0; i<HomeLoc.Count; i++ )
        {
            if (HomeLoc[i].Item1 <= ts)
            {
                index = i;
            }
        }
        return index < 0 ? null : HomeLoc[index].Item2;
    }

    /// <summary>
    /// 清理日志解析器中间数据
    /// </summary>
    public void Clear()
    {
        HomeLoc?.Clear();
        PosData?.Clear();
        UavType = UDevType.UNKNOWN;
        OriTime = DateTime.MinValue;
    }

    /// <summary>
    /// 解析Event日志中的POS数据
    /// 范例：[CAM]:%d,%d,%d,%.3f,%d,%d,%d",SN, Lat, Lng, Alt, Roll, Pitch, Yaw
    /// </summary>
    public static SnapPoint? ParsePos(string strMsg, uint ts)
    {
        var tmp = strMsg.Split([':'], StringSplitOptions.RemoveEmptyEntries);
        if (tmp.Length != 2) return null;
        var seg = tmp[1].Split([','], StringSplitOptions.RemoveEmptyEntries);
        if (seg.Length != 7) return null;

        // 判断是否为有效数据
        var pos = new SnapPoint();
        var isNum = ushort.TryParse(seg[0], out pos.SN);
        if (isNum) isNum = int.TryParse(seg[1], out pos.Lat);
        if (isNum) isNum = int.TryParse(seg[2], out pos.Lng);
        if (isNum) isNum = float.TryParse(seg[3], out pos.Alt);
        if (isNum) isNum = short.TryParse(seg[4], out pos.Roll);
        if (isNum) isNum = short.TryParse(seg[5], out pos.Pitch);
        if (isNum) isNum = ushort.TryParse(seg[6], out pos.Yaw);
        if (isNum) pos.TS = ts;

        return isNum ? pos : null;
    }

    /// <summary>
    /// 解析Event日志文件中的GPS时间，用于绝对时间对齐
    /// </summary>
    private void ParseGPSTime(string info, uint ts)
    {
        // 格式范例 [GPSTime]:2019-1-18 6:26:47
        //         [GPSTime]:2022-9-7 8:22:15 (+8)
        var idx = info.IndexOf(']');
        if (idx < 0) return;

        var subStr = info[(idx + 2)..];
        try
        {
            DateTime gpsTime;
            if (subStr.Contains('(') && subStr.Contains(')'))
            {
                var leftBracketsIdx = subStr.IndexOf('(');
                var strDateTime = subStr[..leftBracketsIdx];
                var strTimeZone = subStr[(leftBracketsIdx + 1)..subStr.IndexOf(')')];
                gpsTime = Convert.ToDateTime(strDateTime);
                // TimeZone处理
                gpsTime = gpsTime.AddHours(Convert.ToInt32(strTimeZone));
            }
            else
            {
                gpsTime = Convert.ToDateTime(subStr);
                // TimeZone处理
                gpsTime = gpsTime.AddHours(TimeZoneInfo.Local.BaseUtcOffset.Hours);
            }

            gpsTime = gpsTime.AddMilliseconds(-ts);

            // 有效性判断(选取时间最新的)
            if (gpsTime > OriTime)
            {
                OriTime = gpsTime;
                Trace.WriteLine($"Find GPSTime {gpsTime:u},OriTime {OriTime:u}");
            }
        }
        catch (Exception e)
        {
            Trace.TraceError(e.ToString());
            // 使用缺省时间
            if (OriTime == DateTime.MinValue)
            {
                OriTime = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            }
        }
    }

    /// <summary>
    /// 解析Event日志文件中版本信息
    /// Demo: [VER]:VTOL V2.2.RC0 4804
    /// </summary>
    private void ParseVersion(string info)
    {
        var subStr = info[6..];
        var segs = subStr.Split([' '], StringSplitOptions.RemoveEmptyEntries);
        if (segs.Length >= 2)
        {
            if ("VTOL" == segs[0]) UavType = UDevType.VTOL;
            if ("COPTER" == segs[0]) UavType = UDevType.COPTER;
            if ("PLANE" == segs[0]) UavType = UDevType.PLANE;
        }
        else
        {
            if (info.Contains("VTOL")) UavType = UDevType.VTOL;
            if (info.Contains("COPTER")) UavType = UDevType.COPTER;
            if (info.Contains("PLANE")) UavType = UDevType.PLANE;
        }
    }

    /// <summary>
    /// 解析Event日志文件中的Home点
    /// </summary>
    private void ParseHomeLoc(string info, uint ts)
    {
        // info 格式为LLA: [Lat/Lng/Alt]
        var tmp = info.Split([':'], StringSplitOptions.RemoveEmptyEntries);
        if (tmp.Length != 2) return;

        tmp = tmp[1].Split([',', ' '], StringSplitOptions.RemoveEmptyEntries);
        if (tmp.Length < 3) return;
        try
        {
            var home = new AbsPos
            {
                Lat = Convert.ToInt32(tmp[0]) / 1e7,
                Lng = Convert.ToInt32(tmp[1]) / 1e7,
                Alt = Convert.ToSingle(tmp[2]) // 单位米
            };
            HomeLoc.Add(Tuple.Create(ts, home));
            Trace.WriteLine($"HOME[{ts}]:{home}");
        }
        catch (Exception)
        {
            throw new ($"Invalid HomeLoc string:{info}");
        }
    }

    /// <summary>
    /// 将时间戳(ms)转换为绝对时间
    /// </summary>
    public DateTime GetDateTime(uint ts)
    {
        return OriTime.AddMilliseconds(ts);
    }

    #region 接口实现
    public override void DoLoad(string fileName)
    {
        using var sr = new StreamReader(fileName);
        Data = new DataTable("EVENT");
        Data.Columns.Add("TS");
        Data.Columns.Add("TIME");
        Data.Columns.Add("MSG");
        Data.Columns.Add("TYPE");
        // [msgtype]-TS:msg

        while (!sr.EndOfStream)
        {
            var line = sr.ReadLine();
            if (string.IsNullOrEmpty(line)) continue;
            var idx = line.IndexOf(":", StringComparison.Ordinal);
            if (idx < 0) continue;
            var strType = line[..idx];
            var strMsg = line[(idx + 1)..];
            var dataRow = Data.NewRow();
            var ts = strType.Split(['-'], StringSplitOptions.RemoveEmptyEntries);

            // 解析消息类型和时间戳
            if (ts.Length != 2) continue;
            dataRow[0] = ts[1];//时间戳
            dataRow[3] = ts[0];//信息类型

            if (strMsg.StartsWith("[HomeLoc]") ||
                strMsg.StartsWith("[HOME]"))
            {// Get Home Location
                ParseHomeLoc(strMsg, Convert.ToUInt32(ts[1]));
            }
            else if (strMsg.StartsWith("[GPSTime]"))
            {// Get GPS Time, 获得时间基准
                ParseGPSTime(strMsg, Convert.ToUInt32(ts[1]));
            }
            else if (strMsg.StartsWith("[CAM]"))
            {// Get CAM info，即POS点数据
                var item = ParsePos(strMsg, Convert.ToUInt32(ts[1]));
                if (null != item) PosData.Add(item);
            }
            else if (strMsg.StartsWith("[VER]"))
            {// Get Version Information
                ParseVersion(strMsg);
            }
            dataRow[2] = strMsg;
            Data.Rows.Add(dataRow);
        }//While

        // 获得绝对时间戳
        foreach (DataRow dr in Data.Rows)
        {
            dr[1] = GetDateTime(Convert.ToUInt32(dr[0])).ToString("yyyy-MM-dd HH:mm:ss.fff");
        }

        WorkDone();
    }
    #endregion
}
